
///
/// This is the implementation of the Handler class
///
#include "stdlib.h"
#include "netinet/in.h"
#include "Handler.h"
#include <cstring>
#include <iostream>
#include <cassert>
#include <string>
#include "Server.h"
#include "Client.h"
#include <cstdio>

Handler::Handler(int fd, std::string& fileName, 
			struct sockaddr_storage clientAddr, socklen_t clientAddrSize) : 
			mSockFD(fd), mDataBuf(NULL), mDataLength(0), mDataBufPos(0),
			mBlockNum(0), mFileName(fileName),	mFilePtr(NULL),  
			mClientAddr(clientAddr), mClientAddrSize(clientAddrSize),
			mLastBlock(false)
{
	///
	/// Since TFTP has a 32MB file size limit, lets read the whole thing
	/// in a buffer all at once
	///
	finished = false;
	mFilePtr = fopen(mFileName.c_str(), "rb");
	if(mFilePtr == NULL)
	{
		// can't open file, send error and exit
		const char msg[] = "Couldn't open file!";
		SendError(ERROR_FILE_NOT_FOUND, msg);
		Server::GetInstance()->HandleError(mSockFD);
	}
	else
	{
		fseek(mFilePtr, 0, SEEK_END);
		mDataLength = ftell(mFilePtr);
		rewind(mFilePtr);

		mDataBuf = new char[mDataLength];
		if(mDataBuf == NULL)
		{
			//oops, unable to allocate buffer
			const char msg[] = "Couldn't allocate buffer. Sorry!";
			SendError(ERROR_NOT_DEFINED, msg);
			fclose(mFilePtr);
			Server::GetInstance()->HandleError(mSockFD);
		}
		else
		{
			if(fread(mDataBuf, 1, mDataLength, mFilePtr) != mDataLength)
			{
				//reading error
				const char msg[] = "Some fread() error. Sorry!";
				SendError(ERROR_NOT_DEFINED, msg);
				fclose(mFilePtr);
				Server::GetInstance()->HandleError(mSockFD);
			}
			else
			{
				fclose(mFilePtr);
				mDataBufPos = 0;
				mBlockNum = 1;
				SendData();
			}
		}
	}
}


Handler::Handler(const Handler& rhs)
{
	mSockFD = rhs.mSockFD;
	memcpy(mMsgBuf, rhs.mMsgBuf, MSG_BUF_SIZE);
	mDataBuf = rhs.mDataBuf;
	mDataLength = rhs.mDataLength;
	mDataBufPos = rhs.mDataBufPos;
	mBlockNum = rhs.mBlockNum;
	mFileName = rhs.mFileName;
	mFilePtr = rhs.mFilePtr;
	mClientAddr = rhs.mClientAddr;
	mClientAddrSize = rhs.mClientAddrSize;
	mLastBlock = rhs.mLastBlock;
}

Handler& Handler::operator=(const Handler& copy)
{
	mSockFD = copy.mSockFD;
	memcpy(mMsgBuf, copy.mMsgBuf, MSG_BUF_SIZE);
	mDataBuf = copy.mDataBuf;
	mDataLength = copy.mDataLength;
	mDataBufPos = copy.mDataBufPos;
	mBlockNum = copy.mBlockNum;
	mFileName = copy.mFileName;
	mFilePtr = copy.mFilePtr;
	mClientAddr = copy.mClientAddr;
	mClientAddrSize = copy.mClientAddrSize;
	mLastBlock = copy.mLastBlock;

	return *this;
}

Handler::~Handler()
{
	// lets leak memory for now

	/*if(mDataBuf != NULL)
		delete[] mDataBuf;*/
}

int Handler::SendData()
{
	uint16_t host, nw;
	
	char* head = mMsgBuf;

	host = OPCODE_DATA;
	nw = htons(host);
	memcpy(head, (char*)&nw, 2);
	head += 2;

	host = mBlockNum;
	nw = htons(host);
	memcpy(head, (char*)&nw, 2);
	head += 2;
	
	unsigned int dataToSend = 
		std::min(mDataLength - mDataBufPos, (unsigned int)DATA_BLOCK_SIZE);

	memcpy(head, mDataBuf + mDataBufPos, dataToSend);
	head += dataToSend;

	int numBytesSent;
	if((numBytesSent = sendto(mSockFD, mMsgBuf, head - mMsgBuf, 0,
					(struct sockaddr*)&mClientAddr, mClientAddrSize)) == -1)
	{
		perror("sendto() error: ");
		exit(1);
		finished = true;
	}

	if(dataToSend < DATA_BLOCK_SIZE)
	{
		std::cout << "Last block sent!" << std::endl;
		mLastBlock = true;
	}
	return 0;
}

int Handler::SendError(ERROR_TYPE err, const char* msg)
{
	uint16_t host, nw;
	
	char* head = mMsgBuf;

	host = OPCODE_ERROR;
	nw = htons(host);
	memcpy(head, (char*)&nw, 2);
	head += 2;

	host = ERROR_FILE_NOT_FOUND;
	nw = htons(host);
	memcpy(head, (char*)&nw, 2);
	head += 2;

	memcpy(head, msg, strlen(msg));
	head += strlen(msg);

	*head = END_BYTE;
	++head;
	
	int numBytesSent;
	if((numBytesSent = sendto(mSockFD, mMsgBuf, head - mMsgBuf, 0,
						(struct sockaddr*)&mClientAddr, mClientAddrSize)) == -1)
	{
		perror("sendto() error: ");
		exit(1);
		finished = true;
	}
	finished = true;
	return 0;
}

int Handler::HandleMessage(const char *packet, const unsigned int packetLength)
{
	uint16_t host, nw;
	const char* head = packet;

	memcpy((char*)&nw, head, 2);
	host = ntohs(nw);
	head += 2;
	
	if(host == OPCODE_ACK)
	{
		memcpy((char*)&nw, head, 2);
		host = ntohs(nw);
		head += 2;

		std::cout << "ACK received for block number: " << host << std::endl;

		if(host != mBlockNum) //ignore this ACK and return
			return 0;

		if(mLastBlock)
		{
			//TODO: last block, wrap up and die
			finished = true;
		}
		else
		{
			//	
			// if previous packet wasn't the last packet, we definitely
			// sent 512 bytes
			//
			mDataBufPos += DATA_BLOCK_SIZE;

			++mBlockNum;

			SendData();
		}
	}
	else if(host == OPCODE_ERROR)
	{
		memcpy((char*)&nw, head, 2);
		host = ntohs(nw);
		head += 2;

		std::cout << "Received error with code: " << host << " and message "
				<< (packet + 4) << std::endl;
		finished = true;
	}

	return 0;
}
